package com.opta.score;

/**
 * 2 * @Author: GuanChong
 * 3 * @Date: 2020/10/12 11:52
 * 4
 */

import com.opta.entity.Line;
import com.opta.entity.OrderSolution;
import com.opta.entity.TaskOrder;
import org.optaplanner.core.api.score.buildin.bendable.BendableScore;
import org.optaplanner.core.impl.score.director.incremental.AbstractIncrementalScoreCalculator;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TaskOrderIncrementalScoreCalculator extends AbstractIncrementalScoreCalculator<OrderSolution> {


    /**
     * 隐藏硬约束 任务物料必须排在可生产的班组上
     */
    private Integer hardScore1;

    /**
     * 拍的越多越好
     */
    private Integer hardScore2;

    /**
     * 不能空产 针对线体？
     */
    private Integer softScore1;
    /**
     * 保证交期，即在最晚开始时间之前排产（软约束）
     */
    private Integer softScore2;

    /**
     * 物料号可生产的班组
     * key:   物料号 materialCode
     * value: 班组   team集合
     */
    private Map<String, Map<String, Double>> materialProducibleTeamMap;


    /**
     * 所有线体
     */
    private List<Line> lines;

    /**
     * 所有任务 增量算法需要设置这个全局变量
     */
    private List<TaskOrder> taskOrders;

    /**
     * 解决方案
     */
    private OrderSolution solution;

    /**
     * 重置方法 清空所有全局变量
     *
     * @param orderSolution
     */
    @Override
    public void resetWorkingSolution(OrderSolution orderSolution) {
        hardScore1 = 0;
        hardScore2 = 0;

        softScore1 = 0;
        softScore2 = 0;

        solution = orderSolution;
        taskOrders = solution.getOrders();
        lines = orderSolution.getLines();
        materialProducibleTeamMap = orderSolution.getMaterialProducibleTeamMap();
        for (TaskOrder order : orderSolution.getOrders()) {
            insert(order);
        }
    }

    @Override
    public void beforeEntityAdded(Object entity) {
        // Do nothing
    }

    @Override
    public void afterEntityAdded(Object entity) {
        insert((TaskOrder) entity);
    }

    @Override
    public void beforeVariableChanged(Object entity, String variableName) {
        TaskOrder order = (TaskOrder) entity;
        switch (variableName) {
            case "line":
            case "sdQty":
                retract(order);
                break;
            default:
                throw new IllegalArgumentException("The variableName (" + variableName + ") is not supported.");
        }
    }

    @Override
    public void afterVariableChanged(Object entity, String variableName) {
        TaskOrder order = (TaskOrder) entity;
        switch (variableName) {
            case "line":
            case "sdQty":
                insert(order);
                break;
            default:
                throw new IllegalArgumentException("The variableName (" + variableName + ") is not supported.");
        }
    }

    @Override
    public void beforeEntityRemoved(Object entity) {
        retract((TaskOrder) entity);
    }

    @Override
    public void afterEntityRemoved(Object entity) {
        // Do nothing
    }


    /**
     * 添加之前插入 一些增进去的全局变量 如产能总量 需要再减去
     *
     * @param
     */
    private void insert(TaskOrder order) {
        if (order.getLine() != null && order.getSdQty() != null && order.getSdQty() > 0) {
            Integer soft1Val = 0;
            hardScore2 = 0;
            //计算hardScore 必须为可生产的机型
            Map<String, Double> teamsMap = materialProducibleTeamMap.get(order.getModelInfos().getMaterialCode());
            if (teamsMap == null || teamsMap.isEmpty()) {
                hardScore1 -= order.getOrderQty();
            } else {
                //该order 的物料号所能生产的班组
                List<String> teams = new ArrayList<>(16);
                teamsMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> teams.add(e.getKey()));
                if (!teams.contains(order.getLine().getTeam())) {
                    hardScore1 -= order.getOrderQty();
                } else {
                    //计算超产能
                    Integer orderQty = order.getSdQty();
                    Line sdLine = order.getLine();
                    Double takt = teamsMap.get(order.getLine().getTeam());
                    BigDecimal orderQtyB = new BigDecimal(orderQty);
                    BigDecimal taktB = new BigDecimal(takt);

                    //已被占用产能
                    Integer lineCpSd = 0;
                    List<TaskOrder> ordersThisLine = taskOrders.stream().filter(s -> s.getLine() != null && s.getLine().getTeam() != null && s.getLine().getTeam().equals(sdLine.getTeam()) && s.getLine().getDate().isEqual(sdLine.getDate()) && !s.getOrderId().equals(order.getOrderId())).collect(Collectors.toList());
                    for (TaskOrder t : ordersThisLine) {
                        Integer sQty = t.getSdQty();
                        Double stakt = teamsMap.get(t.getLine().getTeam());
                        BigDecimal sQtyB = new BigDecimal(sQty);
                        BigDecimal staktB = new BigDecimal(stakt);
                        lineCpSd += sQtyB.multiply(staktB).setScale(0, BigDecimal.ROUND_UP).intValue();
                    }

                    //总产能
                    Integer lineCpAll = sdLine.getCapacity();
                    //剩余产能
                    Integer lineCp = lineCpAll - lineCpSd;
                    //说明数据有问题 sdLine.getTeam() 没有供应
                    if (lineCp <= 0) {
                        //说明已经没有产能了
//                        hardScore2 -= orderQty;
                    } else {
                        Integer orderNeedCp = orderQtyB.multiply(taktB).setScale(0, BigDecimal.ROUND_UP).intValue();
                        //如果剩余产能能排产当前规划数量产能则加分
                        if (lineCp >= orderNeedCp) {
                            //计算全局  hardScore3
                            List<TaskOrder> cpAll = taskOrders.stream().filter(s -> s.getSdQty() != null).collect(Collectors.toList());
                            hardScore2 += cpAll.stream().mapToInt(TaskOrder::getSdQty).sum();
                        } else {
                            hardScore2 += lineCp;
                        }
                    }
                }
            }
            //计算soft2  保证交期 即在最晚开始时间之前排产
            if (order.getLine().getDate().isAfter(order.deliveryDate)) {
                softScore2 -= order.getSdQty();
            }

            //计算soft1  计算空产总数
            List<Line> scheduledLines = taskOrders.stream().map(TaskOrder::getLine).collect(Collectors.toList());
            scheduledLines = scheduledLines.stream().filter(s -> s != null && s.getLineId() != null).collect(Collectors.toList());
            List<String> scheduledLinesStr = scheduledLines.stream().map(s -> s.getLineId()).distinct().collect(Collectors.toList());
            //已排产班组总数 sd
            Integer sd = 0;
            if (scheduledLinesStr != null && !scheduledLinesStr.isEmpty()) {
                sd = scheduledLinesStr.size();
            }
            soft1Val -= (lines.size() - sd);
            softScore1 = soft1Val;
        }
    }


    /**
     * 删除之前回收 一些增进去的全局变量 如产能总量 需要再加上
     *
     * @param order
     */
    private void retract(TaskOrder order) {
        if (order.getLine() != null && order.getSdQty() != null && order.getSdQty() > 0) {
            Integer soft1Val = 0;
            hardScore2 = 0;
            //计算hardScore 必须为可生产的机型
            Map<String, Double> teamsMap = materialProducibleTeamMap.get(order.getModelInfos().getMaterialCode());
            if (teamsMap == null || teamsMap.isEmpty()) {
                hardScore1 += order.getOrderQty();
            } else {
                //该order 的物料号所能生产的班组
                List<String> teams = new ArrayList<>(16);
                teamsMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(e -> teams.add(e.getKey()));
                if (!teams.contains(order.getLine().getTeam())) {
                    hardScore1 += order.getOrderQty();
                } else {
                    //计算超产能
                    Integer orderQty = order.getSdQty();
                    Line sdLine = order.getLine();
                    Double takt = teamsMap.get(order.getLine().getTeam());
                    BigDecimal orderQtyB = new BigDecimal(orderQty);
                    BigDecimal taktB = new BigDecimal(takt);

                    //已被占用产能
                    Integer lineCpSd = 0;
                    List<TaskOrder> ordersThisLine = taskOrders.stream().filter(s -> s.getLine() != null && s.getLine().getTeam() != null && s.getLine().getTeam().equals(sdLine.getTeam()) && s.getLine().getDate().isEqual(sdLine.getDate()) && !s.getOrderId().equals(order.getOrderId())).collect(Collectors.toList());
                    for (TaskOrder t : ordersThisLine) {
                        Integer sQty = t.getSdQty();
                        Double stakt = teamsMap.get(t.getLine().getTeam());
                        BigDecimal sQtyB = new BigDecimal(sQty);
                        BigDecimal staktB = new BigDecimal(stakt);
                        lineCpSd += sQtyB.multiply(staktB).setScale(0, BigDecimal.ROUND_UP).intValue();
                    }

                    //总产能
                    Integer lineCpAll = sdLine.getCapacity();
                    //剩余产能
                    Integer lineCp = lineCpAll - lineCpSd;
                    //说明数据有问题 sdLine.getTeam() 没有供应
                    if (lineCp <= 0) {
                        //说明已经没有产能了
                    } else {
                        Integer orderNeedCp = orderQtyB.multiply(taktB).setScale(0, BigDecimal.ROUND_UP).intValue();
                        //如果剩余产能能排产当前规划数量产能则加分
                        if (lineCp >= orderNeedCp) {
                            //减去当前条再算
                            List<TaskOrder> cpAll = taskOrders.stream().filter(s -> s.getSdQty() != null).collect(Collectors.toList());
                            hardScore2 -= cpAll.stream().mapToInt(TaskOrder::getSdQty).sum();
                        } else {
                            hardScore2 -= lineCp;
                        }
                    }
                }
            }
            //计算soft2  保证交期 即在最晚开始时间之前排产
            if (order.getLine().getDate().isAfter(order.deliveryDate)) {
                softScore2 += order.getSdQty();
            }

            //计算soft1  计算空产总数
            List<Line> scheduledLines = taskOrders.stream().map(TaskOrder::getLine).collect(Collectors.toList());
            scheduledLines = scheduledLines.stream().filter(s -> s != null && s.getLineId() != null).collect(Collectors.toList());
            List<String> scheduledLinesStr = scheduledLines.stream().map(s -> s.getLineId()).distinct().collect(Collectors.toList());
            Integer sd = 0;
            if (scheduledLinesStr != null && !scheduledLinesStr.isEmpty()) {
                sd = scheduledLinesStr.size();
            }
            soft1Val += (lines.size() - sd);
            softScore1 = soft1Val;
        }

    }


    @Override
    public BendableScore calculateScore() {
        int[] hardScores = new int[]{hardScore1, hardScore2};
        int[] softScores = new int[]{softScore1, softScore2};
        return BendableScore.of(hardScores, softScores);
    }
}